package com.yugao.fintech.antelope.auth.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yugao.fintech.antelope.auth.assembly.AuthClientDetailsAssembly;
import com.yugao.fintech.antelope.auth.mapper.OAuth2ClientMapper;
import com.yugao.fintech.antelope.auth.model.entity.OAuth2Client;
import com.yugao.fintech.antelope.auth.model.rqrs.client.*;
import com.yugao.fintech.antelope.auth.oauth2.model.RegisteredClient;
import com.yugao.fintech.antelope.auth.service.AuthMqService;
import com.yugao.fintech.antelope.auth.service.ExtLdpConnectService;
import com.yugao.fintech.antelope.auth.service.OAuth2ClientService;
import com.yugao.fintech.antelope.base.exception.enums.GlobalErrorEnum;
import com.yugao.fintech.antelope.base.model.BasePlusPO;
import com.yugao.fintech.antelope.base.model.enums.AuthErrorEnum;
import com.yugao.fintech.antelope.system.constants.CacheCons;
import com.yugao.fintech.framework.assistant.utils.StringUtils;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import com.yugao.fintech.framework.desensitization.annotations.FieldBindHandle;
import com.yugao.fintech.framework.mybatisplus.utils.PageUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class OAuth2ClientServiceImpl extends ServiceImpl<OAuth2ClientMapper, OAuth2Client> implements OAuth2ClientService {
    @Autowired
    private com.yugao.fintech.antelope.auth.manager.OAuth2ClientManager OAuth2ClientManager;
    @Autowired
    private AuthClientDetailsAssembly authClientDetailsAssembly;
    @Autowired
    private ExtLdpConnectService ldpConnectionService;
    @Autowired
    private AuthMqService mqService;

    @Override
    @Cacheable(value = CacheCons.CLIENT_DETAILS_KEY, key = "#clientId + '_clientId'", unless = "#result == null")
    public OAuth2Client getByClientId(String clientId) {
        return OAuth2ClientManager.getOne(Wrappers.<OAuth2Client>lambdaQuery()
                .eq(OAuth2Client::getClientId, clientId));
    }

    /**
     * 清除客户端缓存
     */
    @Override
    @CacheEvict(value = CacheCons.CLIENT_DETAILS_KEY, allEntries = true)
    public void clearClientCache() {

    }

    /**
     * 根据主键查询 oauth客户端
     *
     * @param id 主键
     * @return 返回记录，没有返回null
     */
    @Override
    @FieldBindHandle
    public OAuthClientResp getClient(Long id) {
        OAuth2Client entity = OAuth2ClientManager.getById(id);
        if (Objects.isNull(entity)) {
            throw new BizException("身份源不存在");
        }
        return authClientDetailsAssembly.toAuthClientDetailsResp(entity);
    }

    @Override
    public List<ClientExtLdpConnResp> listExtLdpConn(Long id) {
        String extGrantTypeStr = Optional.ofNullable(id).map(OAuth2ClientManager::getById)
                .map(OAuth2Client::getExtGrantTypes).orElse("");

        List<String> extGrantTypes = StringUtils.toList(extGrantTypeStr, ",", String.class);

        return ldpConnectionService.list().stream().map(e -> {
            ClientExtLdpConnResp extLdpConn = new ClientExtLdpConnResp();
            extLdpConn.setChecked(extGrantTypes.contains(e.getGrantType()));
            extLdpConn.setDisplayName(e.getDisplayName());
            extLdpConn.setName(e.getName());
            extLdpConn.setGrantType(e.getGrantType());
            extLdpConn.setConfigKey(e.getConfigKey());
            return extLdpConn;
        }).collect(Collectors.toList());
    }

    /**
     * 保存 oauth客户端
     *
     * @param req 新增的记录
     * @return 返回影响行数
     */
    @Override
    public boolean saveClient(OAuthClientSaveReq req) {
        OAuth2Client entity = authClientDetailsAssembly.toAuthClientDetails(req);

        long count = OAuth2ClientManager.count(OAuth2Client.lqw().eq(OAuth2Client::getClientId, req.getClientId()));
        if (count > 0) {
            throw new BizException("客户端已存在");
        }
        return this.save(entity);
    }


    /**
     * 更新 oauth客户端
     *
     * @param req 修改的记录
     * @return 返回影响行数
     */
    @Override
    @CacheEvict(value = CacheCons.CLIENT_DETAILS_KEY, allEntries = true)
    public boolean updateClient(OAuthClientUpdateReq req) {
        OAuth2Client client = this.getById(req.getId());
        if (Objects.isNull(client)) {
            throw new BizException(GlobalErrorEnum.OPERATION_FAIL_SELETE);
        }
        OAuth2Client entity = authClientDetailsAssembly.toAuthClientDetails(req);
        boolean ret = OAuth2ClientManager.updateById(entity);
        mqService.deleteClientAuthority(client.getClientId());
        return ret;
    }

    /**
     * 通过id删除 oauth客户端
     *
     * @param id 删除的主键
     * @return 返回影响行数
     */
    @Override
    @CacheEvict(value = CacheCons.CLIENT_DETAILS_KEY, allEntries = true)
    public boolean deleteClient(Long id) {
        OAuth2Client client = this.getById(id);
        if (Objects.isNull(client)) {
            return true;
        }
        boolean ret = OAuth2ClientManager.removeById(id);
        mqService.deleteClientAuthority(client.getClientId());
        return ret;
    }

    /**
     * 通过ids删除 oauth客户端
     *
     * @param ids 删除的主键
     * @return 返回影响行数
     */
    @Override
    @CacheEvict(value = CacheCons.CLIENT_DETAILS_KEY, allEntries = true)
    public boolean batchDeleteClient(List<Long> ids) {
        List<String> clientIds = this.listByIds(ids).stream().map(OAuth2Client::getClientId).collect(Collectors.toList());
        boolean ret = OAuth2ClientManager.removeByIds(ids);
        clientIds.forEach(mqService::deleteClientAuthority);
        return ret;
    }

    /**
     * 分页查询 oauth客户端
     *
     * @return 返回分页数据
     */
    @Override
    @FieldBindHandle
    public IPage<OAuthClientResp> pageClient(OAuthClientPageReq req) {
        LambdaQueryWrapper<OAuth2Client> queryWrapper = Wrappers.<OAuth2Client>lambdaQuery()
                .like(StringUtils.isNotEmpty(req.getClientId()), OAuth2Client::getClientId, req.getClientId())
                .orderByDesc(BasePlusPO::getCreatedAt);
        IPage<OAuth2Client> pageData = OAuth2ClientManager.page(PageUtil.to(req), queryWrapper);
        return PageUtil.to(pageData, authClientDetailsAssembly::toAuthClientDetailsResp);
    }

    @Override
    public OAuthClientListResp listClient(OAuthClientListReq req) {
        String extGrantType = StringUtils.defaultIfEmpty(req.getExtGrantType(), "-1");
        List<OAuthClientResp> list = this.OAuth2ClientManager.list().stream()
                .map(authClientDetailsAssembly::toAuthClientDetailsResp)
                .collect(Collectors.toList());

        List<Long> checkedList = list.stream()
                .filter(e -> StringUtils.isNotEmpty(e.getExtGrantTypes()))
                .filter(e -> StringUtils.toList(e.getExtGrantTypes(), ",", String.class).contains(extGrantType))
                .map(OAuthClientResp::getId).collect(Collectors.toList());

        OAuthClientListResp resp = new OAuthClientListResp();
        resp.setList(list);
        resp.setCheckedList(checkedList);
        return resp;
    }


    @Override
    public RegisteredClient validOAuthClient(String clientId,
                                             String clientSecret, String authorizedGrantType,
                                             Collection<String> scopes, String redirectUri) {
        OAuth2Client client = this.getByClientId(clientId);
        if (Objects.isNull(client)) {
            throw new BizException(AuthErrorEnum.CLIENT_NOT_EXIST);
        }

        // 校验客户端密钥
        if (StrUtil.isNotEmpty(clientSecret) && ObjectUtil.notEqual(client.getClientSecret(), clientSecret)) {
            throw new BizException(AuthErrorEnum.OAUTH2_CLIENT_CLIENT_SECRET_ERROR);
        }

        // 校验授权方式
        List<String> extGrantTypes = StringUtils.toList(client.getExtGrantTypes(), ",", String.class);
        client.getAuthorizedGrantTypes().addAll(extGrantTypes);
        if (StrUtil.isNotEmpty(authorizedGrantType) && !CollUtil.contains(client.getAuthorizedGrantTypes(), authorizedGrantType)) {
            throw new BizException(AuthErrorEnum.OAUTH2_GRANT_TYPE_NOT_EXISTS, authorizedGrantType);
        }

        // 校验授权范围
        if (CollUtil.isNotEmpty(scopes) && !CollUtil.containsAll(client.getScope(), scopes)) {
            throw new BizException(AuthErrorEnum.OAUTH2_CLIENT_SCOPE_OVER);
        }

        RegisteredClient registeredClient = RegisteredClient.of(client);
        Optional.ofNullable(scopes).ifPresent(
                scope -> scope.stream().filter(StrUtil::isNotBlank).forEach(registeredClient::scope));

        return registeredClient;
    }

    @Override
    @CacheEvict(value = CacheCons.CLIENT_DETAILS_KEY, allEntries = true)
    public void refreshClientAuthorityByRoleIds(List<Long> roleIds, boolean roleDeleted) {
        List<OAuth2Client> list = this.list();
        List<String> clientIds = new ArrayList<>();
        for (OAuth2Client client : list) {
            List<Long> clientRoleIds = Optional.ofNullable(client.getRoleIds()).orElse(Collections.emptyList());
            if (clientRoleIds.stream().anyMatch(roleIds::contains)) {
                if (roleDeleted) {
                    clientRoleIds.removeAll(roleIds);
                }
                clientIds.add(client.getClientId());
                client.setRoleIds(clientRoleIds);
            }
        }
        this.updateBatchById(list);
        clientIds.forEach(mqService::deleteClientAuthority);
    }
}
